package com.qzl.server.handler;

import com.qzl.common.netty.config.ProtocolConstants;
import com.qzl.common.netty.protocol.MessageCodec;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ProtocolSwitchHandler extends ChannelInboundHandlerAdapter {
    // 标志位
    private static final AttributeKey<Boolean> PROTOCOL_DETERMINED = AttributeKey.valueOf("protocol.determined");

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 如果已经完成协议切换，直接传递给后续 handler 处理
        if (Boolean.TRUE.equals(ctx.channel().attr(PROTOCOL_DETERMINED).get())) {
            ctx.fireChannelRead(msg);
            return;
        }
        if (msg instanceof ByteBuf) {
            ByteBuf buf = (ByteBuf) msg;
            // 是否websocket协议
            if (isWebSocketRequest(buf)) {
                // socket协议
                getWebsocketPipeline(ctx);
                ctx.channel().attr(PROTOCOL_DETERMINED).set(true); // 设置标志位
            } else {
                // 自定义协议头
                // 设置协议为自定义 TCP
                ctx.channel().attr(AttributeKey.valueOf(ProtocolConstants.PROTOCOL_KEY))
                        .set(ProtocolConstants.PROTOCOL_CUSTOM_TCP);
                ctx.pipeline()
                        .addAfter(ctx.name(), "messageCodec", new MessageCodec());
                ctx.channel().attr(PROTOCOL_DETERMINED).set(true); // 设置标志位
            }
        } else if (msg instanceof FullHttpRequest) {
            // websocket 协议
            FullHttpRequest request = (FullHttpRequest) msg;
            // 手动判断是否为 WebSocket 握手请求（Netty 4 兼容写法）
            String upgradeHeader = request.headers().get("Upgrade");
            boolean isWebSocket = upgradeHeader != null && upgradeHeader.equalsIgnoreCase("websocket");
            if (isWebSocket) {
                // 如果是 WebSocket 请求，则继续交给 WebSocket 处理器处理
                getWebsocketPipeline(ctx);
                ctx.channel().attr(PROTOCOL_DETERMINED).set(true);
            } else {
                log.info("未知协议");
            }
        } else {
            log.info("未知协议");
        }
        // 继续传递事件给下一个处理器
        ctx.fireChannelRead(msg);
    }

    private void getWebsocketPipeline(ChannelHandlerContext ctx) {
        if (ctx.pipeline().get("webSocketHandler") == null) {
            // 设置协议为 WebSocket
            ctx.channel().attr(AttributeKey.valueOf(ProtocolConstants.PROTOCOL_KEY))
                    .set(ProtocolConstants.PROTOCOL_WEBSOCKET);
            ctx.pipeline()
                    .addAfter(ctx.name(), "httpCodec", new HttpServerCodec())
                    .addAfter("httpCodec", "aggregator", new HttpObjectAggregator(65536))
                    .addAfter("aggregator", "webSocketHandler", new WebSocketServerProtocolHandler("/ws"))
                    .addAfter("webSocketHandler", "webSocketBusinessHandler", new WebSocketToMessageHandler());

        }
    }

    /**
     * 判断是否为 WebSocket 请求
     * 只有在首次建立连接时发送是http请求之后是二进制数据无法判断
     */
    private boolean isWebSocketRequest(ByteBuf buf) {
        int readerIndex = buf.readerIndex();
        int readableBytes = buf.readableBytes();

        if (readableBytes < 16) { // 最小长度不足以构成一个有效 HTTP 请求头
            return false;
        }

        byte[] bytes = new byte[Math.min(buf.readableBytes(), 512)];
        buf.readBytes(bytes, 0, bytes.length);

        String httpRequest = new String(bytes);

        // 检查是否包含 WebSocket 握手的关键字段
        boolean isGet = httpRequest.startsWith("GET ");
        boolean hasUpgrade = httpRequest.contains("Upgrade: websocket") || httpRequest.contains("upgrade: websocket");
        boolean hasConnectionUpgrade = httpRequest.contains("Connection: Upgrade") || httpRequest.contains("connection: upgrade");

        buf.readerIndex(readerIndex); // 重置读指针，避免影响后续处理

        return isGet && hasUpgrade && hasConnectionUpgrade;
    }

}
